//
// Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
// OPC client, server and subscriber examples in C# on GitHub: https://github.com/OPCLabs/Examples-OPCStudio-CSharp .
// Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
// a commercial license in order to use Online Forums, and we reply to every post.
using System;
using OpcLabs.EasyOpc.UA.AddressSpace.Standard;
using OpcLabs.EasyOpc.UA.NodeSpace;
namespace UAServerDemoLibrary
{
static public class DataNodes
{
/// <summary>
/// Adds static and dynamic nodes that demonstrate various data types and access levels.
/// </summary>
/// <param name="parentFolder">The folder to which to add the nodes.</param>
static public void AddToParent(UAFolder parentFolder)
{
// Create Data folder.
UAFolder dataFolder = UAFolder.CreateIn(parentFolder, "Data");
// Create read-only data variables of various data types, without adding them to the server first. We store
// references to them individually, because we later implement write-only variables that write to these
// read-only variables.
UADataVariable booleanReadOnlyDataVariable =
new UADataVariable("BooleanValue").Writable(false).ValueType<bool>();
UADataVariable byteStringReadOnlyDataVariable =
new UADataVariable("ByteStringValue").Writable(false).ValueType<byte[]>();
UADataVariable byteReadOnlyDataVariable = new UADataVariable("ByteValue").Writable(false).ValueType<byte>();
UADataVariable dateTimeReadOnlyDataVariable =
new UADataVariable("DateTimeValue").Writable(false).ValueType<DateTime>();
UADataVariable doubleReadOnlyDataVariable =
new UADataVariable("DoubleValue").Writable(false).ValueType<double>();
UADataVariable floatReadOnlyDataVariable =
new UADataVariable("FloatValue").Writable(false).ValueType<float>();
UADataVariable guidReadOnlyDataVariable = new UADataVariable("GuidValue").Writable(false).ValueType<Guid>();
UADataVariable int16ReadOnlyDataVariable =
new UADataVariable("Int16Value").Writable(false).ValueType<short>();
UADataVariable int32ReadOnlyDataVariable =
new UADataVariable("Int32Value").Writable(false).ValueType<int>();
UADataVariable int64ReadOnlyDataVariable =
new UADataVariable("Int64Value").Writable(false).ValueType<long>();
UADataVariable sByteReadOnlyDataVariable =
new UADataVariable("SByteValue").Writable(false).ValueType<sbyte>();
UADataVariable stringReadOnlyDataVariable =
new UADataVariable("StringValue").Writable(false).ValueType<string>();
UADataVariable uInt16ReadOnlyDataVariable =
new UADataVariable("UInt16Value").Writable(false).ValueType<ushort>();
UADataVariable uInt32ReadOnlyDataVariable =
new UADataVariable("UInt32Value").Writable(false).ValueType<uint>();
UADataVariable uInt64ReadOnlyDataVariable =
new UADataVariable("UInt64Value").Writable(false).ValueType<ulong>();
UADataVariable variantReadOnlyDataVariable =
new UADataVariable("VariantValue").Writable(false);
// Create Constant sub-folder under the Data folder. It contains read-only data variables with constant values.
dataFolder.Add(
new UAFolder("Constant")
{
new UAFolder("Scalar")
{
new UADataVariable("BooleanValue").ConstantValue(true),
new UADataVariable("ByteStringValue").ConstantValue(new byte[] { 0x57, 0x21, 0x40, 0xfc }),
new UADataVariable("ByteValue").ConstantValue((byte)144),
new UADataVariable("DateTimeValue").ConstantValue(
// We are passing in UTC times, because we want always the same result, and so we must specify
// the DateTimeKind. You can pass in local times, but then they will be converted to UTC by the
// server, and the result will depend on the time zone.
DateTime.SpecifyKind(new DateTime(2024, 7, 12, 14, 4, 55).AddSeconds(0.444),
DateTimeKind.Utc)),
new UADataVariable("DoubleValue").ConstantValue(7.75630105797e-011),
new UADataVariable("FloatValue").ConstantValue(2.77002e+29f),
new UADataVariable("GuidValue").ConstantValue(
new Guid("{1AEF59AE-5029-42A7-9AE2-B2DC00072999}")),
new UADataVariable("Int16Value").ConstantValue((short)-30956),
new UADataVariable("Int32Value").ConstantValue(276673160),
new UADataVariable("Int64Value").ConstantValue(1412096336825367659),
new UADataVariable("SByteValue").ConstantValue((sbyte)-113),
new UADataVariable("StringValue").ConstantValue("lorem ipsum"),
new UADataVariable("UInt16Value").ConstantValue((ushort)64421),
new UADataVariable("UInt32Value").ConstantValue(3853116537U),
new UADataVariable("UInt64Value").ConstantValue(9431348106520835314UL),
new UADataVariable("VariantValue").ConstantValue(529739609)
}
});
// Create Dynamic sub-folder under the Data folder. It contains data variables with dynamically changing values.
dataFolder.Add(
new UAFolder("Dynamic")
{
new UAFolder("Array")
{
new UADataVariable("BooleanValue").ReadValueFunction(() => NextRandomArray(NextRandomBoolean)),
new UADataVariable("ByteStringValue").ReadValueFunction(() =>
NextRandomArray(NextRandomByteString)),
// This is a tricky case. We want array of Byte-s, but that is automatically recognized as scalar
// OPC UA ByteString. For a true array of Byte-s, the data type Id and array dimension list must be
// specified explicitly.
new UADataVariable("ByteValue").ReadValueFunction(
dataTypeId: UADataTypeIds.Byte,
arrayRank: 1,
() => NextRandomArray(NextRandomByte)),
new UADataVariable("DateTimeValue").ReadValueFunction(() =>
NextRandomArray(NextRandomDateTime)),
new UADataVariable("DoubleValue").ReadValueFunction(() => NextRandomArray(NextRandomDouble)),
new UADataVariable("FloatValue").ReadValueFunction(() => NextRandomArray(NextRandomFloat)),
new UADataVariable("GuidValue").ReadValueFunction(() => NextRandomArray(NextRandomGuid)),
new UADataVariable("Int16Value").ReadValueFunction(() => NextRandomArray(NextRandomInt16)),
new UADataVariable("Int32Value").ReadValueFunction(() => NextRandomArray(NextRandomInt32)),
new UADataVariable("Int64Value").ReadValueFunction(() => NextRandomArray(NextRandomInt64)),
new UADataVariable("SByteValue").ReadValueFunction(() => NextRandomArray(NextRandomSByte)),
new UADataVariable("StringValue").ReadValueFunction(() => NextRandomArray(NextRandomString)),
new UADataVariable("UInt16Value").ReadValueFunction(() => NextRandomArray(NextRandomUInt16)),
new UADataVariable("UInt32Value").ReadValueFunction(() => NextRandomArray(NextRandomUInt32)),
new UADataVariable("UInt64Value").ReadValueFunction(() => NextRandomArray(NextRandomUInt64)),
new UADataVariable("VariantValue").ReadValueFunction(() => NextRandomArray(NextRandomVariant))
},
new UAFolder("Scalar")
{
new UADataVariable("BooleanValue").ReadValueFunction(NextRandomBoolean),
new UADataVariable("ByteStringValue").ReadValueFunction(NextRandomByteString),
new UADataVariable("ByteValue").ReadValueFunction(NextRandomByte),
new UADataVariable("DateTimeValue").ReadValueFunction(NextRandomDateTime),
new UADataVariable("DoubleValue").ReadValueFunction(NextRandomDouble),
new UADataVariable("FloatValue").ReadValueFunction(NextRandomFloat),
new UADataVariable("GuidValue").ReadValueFunction(NextRandomGuid),
new UADataVariable("Int16Value").ReadValueFunction(NextRandomInt16),
new UADataVariable("Int32Value").ReadValueFunction(NextRandomInt32),
new UADataVariable("Int64Value").ReadValueFunction(NextRandomInt64),
new UADataVariable("SByteValue").ReadValueFunction(NextRandomSByte),
new UADataVariable("StringValue").ReadValueFunction(NextRandomString),
new UADataVariable("UInt16Value").ReadValueFunction(NextRandomUInt16),
new UADataVariable("UInt32Value").ReadValueFunction(NextRandomUInt32),
new UADataVariable("UInt64Value").ReadValueFunction(NextRandomUInt64),
new UADataVariable("VariantValue").ReadValueFunction(NextRandomVariant)
}
});
// The FullyWritable sub-folder contains data variables that have not only writable value, but also writable
// source timestamp and status code.
dataFolder.Add(
new UAFolder("FullyWritable")
{
new UAFolder("Scalar")
{
new UADataVariable("BooleanValue").ReadWriteValue(true)
.Writable(true, true, true),
new UADataVariable("ByteStringValue").ReadWriteValue(new byte[] { 0x57, 0x21, 0x40, 0xfc })
.Writable(true, true, true),
new UADataVariable("ByteValue").ReadWriteValue((byte)144)
.Writable(true, true, true),
new UADataVariable("DateTimeValue").ReadWriteValue(
// We are passing in UTC times, because we want always the same result, and so we must specify
// the DateTimeKind. You can pass in local times, but then they will be converted to UTC by the
// server, and the result will depend on the time zone.
DateTime.SpecifyKind(new DateTime(2024, 7, 12, 14, 4, 55).AddSeconds(0.444),
DateTimeKind.Utc))
.Writable(true, true, true),
new UADataVariable("DoubleValue").ReadWriteValue(7.75630105797e-011)
.Writable(true, true, true),
new UADataVariable("FloatValue").ReadWriteValue(2.77002e+29f)
.Writable(true, true, true),
new UADataVariable("GuidValue")
.ReadWriteValue(new Guid("{1AEF59AE-5029-42A7-9AE2-B2DC00072999}"))
.Writable(true, true, true),
new UADataVariable("Int16Value").ReadWriteValue((short)-30956)
.Writable(true, true, true),
new UADataVariable("Int32Value").ReadWriteValue(276673160)
.Writable(true, true, true),
new UADataVariable("Int64Value").ReadWriteValue(1412096336825367659)
.Writable(true, true, true),
new UADataVariable("SByteValue").ReadWriteValue((sbyte)-113)
.Writable(true, true, true),
new UADataVariable("StringValue").ReadWriteValue("lorem ipsum")
.Writable(true, true, true),
new UADataVariable("UInt16Value").ReadWriteValue((ushort)64421)
.Writable(true, true, true),
new UADataVariable("UInt32Value").ReadWriteValue(3853116537U)
.Writable(true, true, true),
new UADataVariable("UInt64Value").ReadWriteValue(9431348106520835314UL)
.Writable(true, true, true),
new UADataVariable("VariantValue").ReadWriteValue(529739609)
.Writable(true, true, true)
}
});
// The ReadOnly sub-folder contains data variables that are read-only, and their values can be changed through
// corresponding data variables in the WriteOnly sub-folder.
dataFolder.Add(
new UAFolder("ReadOnly")
{
new UAFolder("Scalar")
{
booleanReadOnlyDataVariable,
byteStringReadOnlyDataVariable,
byteReadOnlyDataVariable,
dateTimeReadOnlyDataVariable,
doubleReadOnlyDataVariable,
floatReadOnlyDataVariable,
guidReadOnlyDataVariable,
int16ReadOnlyDataVariable,
int32ReadOnlyDataVariable,
int64ReadOnlyDataVariable,
sByteReadOnlyDataVariable,
stringReadOnlyDataVariable,
uInt16ReadOnlyDataVariable,
uInt32ReadOnlyDataVariable,
uInt64ReadOnlyDataVariable,
variantReadOnlyDataVariable
}
});
// The Static sub-folder contains data variables with static values which can be changed through writing to
// them (so-called "registers").
dataFolder.Add(
new UAFolder("Static")
{
// For demonstration, we consistently create one-dimensional arrays with initially 3 elements, where the
// first element has the same value as the scalar variable with the same name.
new UAFolder("Array")
{
new UADataVariable("BooleanValue").ReadWriteValue(new[]
{
true,
false,
true
}),
new UADataVariable("ByteStringValue").ReadWriteValue(new[]
{
new byte[] { 0x57, 0x21, 0x40, 0xfc },
new byte[] { 248, 131, 217, 210 },
new byte[] { 252, 152, 119, 65 }
}),
// This is a tricky case. We want array of Byte-s, but that is automatically recognized as scalar
// OPC UA ByteString. For a true array of Byte-s, the data type Id and array dimension list must be
// specified explicitly.
new UADataVariable("ByteValue").ReadWriteValue(
dataTypeId: UADataTypeIds.Byte,
arrayRank: 1,
value: new byte[]
{
144,
19,
233
}),
new UADataVariable("DateTimeValue").ReadWriteValue(new[]
{
// We are passing in UTC times, because we want always the same result, and so we must specify
// the DateTimeKind. You can pass in local times, but then they will be converted to UTC by the
// server, and the result will depend on the time zone.
DateTime.SpecifyKind(new DateTime(2024, 7, 12, 14, 4, 55).AddSeconds(0.444),
DateTimeKind.Utc),
DateTime.SpecifyKind(new DateTime(2024, 4, 8), DateTimeKind.Utc),
DateTime.SpecifyKind(new DateTime(2023, 8, 14, 18, 13, 0), DateTimeKind.Utc)
}),
new UADataVariable("DoubleValue").ReadWriteValue(new[]
{
7.75630105797e-011,
-0.467227097818268,
-3.51653052582609E+300
}),
new UADataVariable("FloatValue").ReadWriteValue(new[]
{
2.77002e+29f,
-1.103936E+36f,
-9.002293E-28f
}),
new UADataVariable("GuidValue").ReadWriteValue(new[]
{
new Guid("{1AEF59AE-5029-42A7-9AE2-B2DC00072999}"),
new Guid("{E8690EA3-25D0-4F19-9DFC-AA25D2772B2F}"),
new Guid("{9E081C84-7953-4A88-B709-447FC187EDD9}"),
}),
new UADataVariable("Int16Value").ReadWriteValue(new short[]
{
-30956,
31277,
21977
}),
new UADataVariable("Int32Value").ReadWriteValue(new[]
{
276673160,
630080334,
-391755284
}),
new UADataVariable("Int64Value").ReadWriteValue(new[]
{
1412096336825367659,
-808781653700434592,
4707848393174903135
}),
new UADataVariable("SByteValue").ReadWriteValue(new sbyte[]
{
-113,
-92,
2
}),
new UADataVariable("StringValue").ReadWriteValue(new[]
{
"lorem ipsum",
"dolor sit amet",
"consectetur adipiscing elit"
}),
new UADataVariable("UInt16Value").ReadWriteValue(new ushort[]
{
64421,
22663,
36755
}),
new UADataVariable("UInt32Value").ReadWriteValue(new uint[]
{
3853116537,
968679231,
995611904
}),
new UADataVariable("UInt64Value").ReadWriteValue(new ulong[]
{
9431348106520835314,
15635738044048254300,
946287779964705249
}),
new UADataVariable("VariantValue").ReadWriteValue(new object[]
{
529739609,
"lorem ipsum",
new Guid("{1AEF59AE-5029-42A7-9AE2-B2DC00072999}")
})
},
// We create 2-dimensional arrays with 4x3 size, and default element values.
new UAFolder("Array2D")
{
new UADataVariable("BooleanValue").ReadWriteValue(new bool[4, 3]),
new UADataVariable("ByteStringValue").ReadWriteValue(new byte[4, 3][]),
new UADataVariable("ByteValue").ReadWriteValue(new byte[4, 3]),
new UADataVariable("DateTimeValue").ReadWriteValue(new DateTime[4, 3]),
new UADataVariable("DoubleValue").ReadWriteValue(new double[4, 3]),
new UADataVariable("FloatValue").ReadWriteValue(new float[4, 3]),
new UADataVariable("GuidValue").ReadWriteValue(new Guid[4, 3]),
new UADataVariable("Int16Value").ReadWriteValue(new short[4, 3]),
new UADataVariable("Int32Value").ReadWriteValue(new int[4, 3]),
new UADataVariable("Int64Value").ReadWriteValue(new long[4, 3]),
new UADataVariable("SByteValue").ReadWriteValue(new sbyte[4, 3]),
new UADataVariable("StringValue").ReadWriteValue(new string[4, 3]),
new UADataVariable("UInt16Value").ReadWriteValue(new ushort[4, 3]),
new UADataVariable("UInt32Value").ReadWriteValue(new uint[4, 3]),
new UADataVariable("UInt64Value").ReadWriteValue(new ulong[4, 3]),
new UADataVariable("VariantValue").ReadWriteValue(new object[4, 3])
},
// Array nodes with specified and enforced maximum array dimensions.
new UAFolder("BoundedArray")
{
new UADataVariable("BooleanValue").ReadWriteValue(new bool[4], arrayDimensions: 5),
new UADataVariable("ByteStringValue").ReadWriteValue(new byte[4][], arrayDimensions: 5),
// This is a tricky case. We want array of Byte-s, but that is automatically recognized as scalar
// OPC UA ByteString. For a true array of Byte-s, the data type Id and array dimension list must be
// specified explicitly.
new UADataVariable("ByteValue").ReadWriteValue(
dataTypeId: UADataTypeIds.Byte,
arrayDimensionList: new[] { 5 },
new byte[4]),
new UADataVariable("DateTimeValue").ReadWriteValue(new DateTime[4], arrayDimensions: 5),
new UADataVariable("DoubleValue").ReadWriteValue(new double[4], arrayDimensions: 5),
new UADataVariable("FloatValue").ReadWriteValue(new float[4], arrayDimensions: 5),
new UADataVariable("GuidValue").ReadWriteValue(new Guid[4], arrayDimensions: 5),
new UADataVariable("Int16Value").ReadWriteValue(new short[4], arrayDimensions: 5),
new UADataVariable("Int32Value").ReadWriteValue(new int[4], arrayDimensions: 5),
new UADataVariable("Int64Value").ReadWriteValue(new long[4], arrayDimensions: 5),
new UADataVariable("SByteValue").ReadWriteValue(new sbyte[4], arrayDimensions: 5),
new UADataVariable("StringValue").ReadWriteValue(new string[4], arrayDimensions: 5),
new UADataVariable("UInt16Value").ReadWriteValue(new ushort[4], arrayDimensions: 5),
new UADataVariable("UInt32Value").ReadWriteValue(new uint[4], arrayDimensions: 5),
new UADataVariable("UInt64Value").ReadWriteValue(new ulong[4], arrayDimensions: 5),
new UADataVariable("VariantValue").ReadWriteValue(new object[4], arrayDimensions: 5)
},
new UAFolder("Scalar")
{
new UADataVariable("BooleanValue").ReadWriteValue(true),
new UADataVariable("ByteStringValue").ReadWriteValue(new byte[] { 0x57, 0x21, 0x40, 0xfc }),
new UADataVariable("ByteValue").ReadWriteValue((byte)144),
new UADataVariable("DateTimeValue").ReadWriteValue(
// We are passing in UTC times, because we want always the same result, and so we must specify
// the DateTimeKind. You can pass in local times, but then they will be converted to UTC by the
// server, and the result will depend on the time zone.
DateTime.SpecifyKind(new DateTime(2024, 7, 12, 14, 4, 55).AddSeconds(0.444),
DateTimeKind.Utc)),
new UADataVariable("DoubleValue").ReadWriteValue(7.75630105797e-011),
new UADataVariable("FloatValue").ReadWriteValue(2.77002e+29f),
new UADataVariable("GuidValue").ReadWriteValue(
new Guid("{1AEF59AE-5029-42A7-9AE2-B2DC00072999}")),
new UADataVariable("Int16Value").ReadWriteValue((short)-30956),
new UADataVariable("Int32Value").ReadWriteValue(276673160),
new UADataVariable("Int64Value").ReadWriteValue(1412096336825367659),
new UADataVariable("SByteValue").ReadWriteValue((sbyte)-113),
new UADataVariable("StringValue").ReadWriteValue("lorem ipsum"),
new UADataVariable("UInt16Value").ReadWriteValue((ushort)64421),
new UADataVariable("UInt32Value").ReadWriteValue(3853116537U),
new UADataVariable("UInt64Value").ReadWriteValue(9431348106520835314UL),
new UADataVariable("VariantValue").ReadWriteValue(529739609)
}
});
// Create and add write-only data variables of various data types. Implement write actions that write the value
// to the corresponding read-only data variable of the same data type.
dataFolder.Add(
new UAFolder("WriteOnly")
{
new UAFolder("Scalar")
{
new UADataVariable("BooleanValue").Readable(false).WriteValueAction((bool value) =>
booleanReadOnlyDataVariable.UpdateReadAttributeData(value)),
new UADataVariable("ByteStringValue").Readable(false).WriteValueAction((byte[] value) =>
byteStringReadOnlyDataVariable.UpdateReadAttributeData(value)),
new UADataVariable("ByteValue").Readable(false).WriteValueAction((byte value) =>
byteReadOnlyDataVariable.UpdateReadAttributeData(value)),
new UADataVariable("DateTimeValue").Readable(false).WriteValueAction((DateTime value) =>
dateTimeReadOnlyDataVariable.UpdateReadAttributeData(value)),
new UADataVariable("DoubleValue").Readable(false).WriteValueAction((double value) =>
doubleReadOnlyDataVariable.UpdateReadAttributeData(value)),
new UADataVariable("FloatValue").Readable(false).WriteValueAction((float value) =>
floatReadOnlyDataVariable.UpdateReadAttributeData(value)),
new UADataVariable("GuidValue").Readable(false).WriteValueAction((Guid value) =>
guidReadOnlyDataVariable.UpdateReadAttributeData(value)),
new UADataVariable("Int16Value").Readable(false).WriteValueAction((short value) =>
int16ReadOnlyDataVariable.UpdateReadAttributeData(value)),
new UADataVariable("Int32Value").Readable(false).WriteValueAction((int value) =>
int32ReadOnlyDataVariable.UpdateReadAttributeData(value)),
new UADataVariable("Int64Value").Readable(false).WriteValueAction((long value) =>
int64ReadOnlyDataVariable.UpdateReadAttributeData(value)),
new UADataVariable("SByteValue").Readable(false).WriteValueAction((sbyte value) =>
sByteReadOnlyDataVariable.UpdateReadAttributeData(value)),
new UADataVariable("StringValue").Readable(false).WriteValueAction((string value) =>
stringReadOnlyDataVariable.UpdateReadAttributeData(value)),
new UADataVariable("UInt16Value").Readable(false).WriteValueAction((ushort value) =>
uInt16ReadOnlyDataVariable.UpdateReadAttributeData(value)),
new UADataVariable("UInt32Value").Readable(false).WriteValueAction((uint value) =>
uInt32ReadOnlyDataVariable.UpdateReadAttributeData(value)),
new UADataVariable("UInt64Value").Readable(false).WriteValueAction((ulong value) =>
uInt64ReadOnlyDataVariable.UpdateReadAttributeData(value)),
new UADataVariable("VariantValue").Readable(false).WriteValueAction((object value) =>
variantReadOnlyDataVariable.UpdateReadAttributeData(value))
}
});
}
// Random value generators.
static private readonly Random Random = new Random();
static private readonly string[] RandomStrings = new[] { "lorem", "ipsum", "dolor", "sit", "amet" };
static private T[] NextRandomArray<T>(Func<T> nextRandomElement) =>
new[] { nextRandomElement(), nextRandomElement(), nextRandomElement() };
static private bool NextRandomBoolean() => Random.Next(2) != 0;
static private byte NextRandomByte() => (byte)Random.Next(byte.MinValue, byte.MaxValue + 1);
static private byte[] NextRandomByteString() =>
new[] { NextRandomByte(), NextRandomByte(), NextRandomByte(), NextRandomByte() };
static private DateTime NextRandomDateTime() =>
DateTime.MinValue.AddMilliseconds((DateTime.MaxValue - DateTime.MinValue).TotalMilliseconds *
Random.NextDouble());
static private float NextRandomFloat() =>
(float)Math.Pow(10, Math.Log10(float.MaxValue) * Random.NextDouble()) * (2 * Random.Next(2) - 1);
static private double NextRandomDouble() =>
Math.Pow(10, Math.Log10(double.MaxValue) * Random.NextDouble()) * (2 * Random.Next(2) - 1);
static private Guid NextRandomGuid() => Guid.NewGuid();
static private short NextRandomInt16() => (short)Random.Next(short.MinValue, short.MaxValue + 1);
static private int NextRandomInt32()
{
byte[] buffer = new byte[4];
Random.NextBytes(buffer);
return BitConverter.ToInt32(buffer, 0);
}
static private long NextRandomInt64()
{
byte[] buffer = new byte[8];
Random.NextBytes(buffer);
return BitConverter.ToInt64(buffer, 0);
}
static private sbyte NextRandomSByte() => (sbyte)Random.Next(sbyte.MinValue, sbyte.MaxValue + 1);
static private string NextRandomString() => RandomStrings[Random.Next(RandomStrings.Length)];
static private ushort NextRandomUInt16() => (ushort)Random.Next(ushort.MinValue, ushort.MaxValue + 1);
static private uint NextRandomUInt32()
{
byte[] buffer = new byte[4];
Random.NextBytes(buffer);
return BitConverter.ToUInt32(buffer, 0);
}
static private ulong NextRandomUInt64()
{
byte[] buffer = new byte[8];
Random.NextBytes(buffer);
return BitConverter.ToUInt64(buffer, 0);
}
static private object NextRandomVariant()
{
switch (Random.Next(15))
{
case 0:
return NextRandomBoolean();
case 1:
return NextRandomByteString();
case 2:
return NextRandomByte();
case 3:
return NextRandomDateTime();
case 4:
return NextRandomDouble();
case 5:
return NextRandomFloat();
case 6:
return NextRandomGuid();
case 7:
return NextRandomInt16();
case 8:
return NextRandomInt32();
case 9:
return NextRandomInt64();
case 10:
return NextRandomSByte();
case 11:
return NextRandomString();
case 12:
return NextRandomUInt16();
case 13:
return NextRandomUInt32();
case 14:
return NextRandomUInt64();
default:
return null;
}
}
}
}
'
' Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
' OPC client and subscriber examples in VB.NET on GitHub: https://github.com/OPCLabs/Examples-QuickOPC-VBNET .
' Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
' a commercial license in order to use Online Forums, and we reply to every post.
Imports System
Imports System.Reactive
Imports Newtonsoft.Json.Linq
Imports OpcLabs.EasyOpc.AlarmsAndEvents.Engine
Imports OpcLabs.EasyOpc.UA.AddressSpace.Standard
Imports OpcLabs.EasyOpc.UA.NodeSpace
'Namespace UAServerDemoLibrary
Public Module DataNodes
''' <summary>
''' Adds static and dynamic nodes that demonstrate various data types and access levels.
''' </summary>
''' <param name="parentFolder">The folder to which to add the nodes.</param>
Sub AddToParent(parentFolder As UAFolder)
' Create Data folder.
Dim dataFolder = UAFolder.CreateIn(parentFolder, "Data")
' Create read-only data variables of various data types, without adding them to the server first. We store
' references to them individually, because we later implement write-only variables that write to these
' read-only variables.
Dim booleanReadOnlyDataVariable As UADataVariable =
New UADataVariable("BooleanValue").Writable(False).ValueType(Of Boolean)()
Dim byteStringReadOnlyDataVariable As UADataVariable =
New UADataVariable("ByteStringValue").Writable(False).ValueType(Of Byte())()
Dim byteReadOnlyDataVariable As UADataVariable = New UADataVariable("ByteValue").Writable(False).ValueType(Of Byte)()
Dim dateTimeReadOnlyDataVariable As UADataVariable =
New UADataVariable("DateTimeValue").Writable(False).ValueType(Of DateTime)()
Dim doubleReadOnlyDataVariable As UADataVariable =
New UADataVariable("DoubleValue").Writable(False).ValueType(Of Double)()
Dim floatReadOnlyDataVariable As UADataVariable =
New UADataVariable("FloatValue").Writable(False).ValueType(Of Single)()
Dim guidReadOnlyDataVariable As UADataVariable = New UADataVariable("GuidValue").Writable(False).ValueType(Of Guid)()
Dim int16ReadOnlyDataVariable As UADataVariable =
New UADataVariable("Int16Value").Writable(False).ValueType(Of Short)()
Dim int32ReadOnlyDataVariable As UADataVariable =
New UADataVariable("Int32Value").Writable(False).ValueType(Of Integer)()
Dim int64ReadOnlyDataVariable As UADataVariable =
New UADataVariable("Int64Value").Writable(False).ValueType(Of Long)()
Dim sByteReadOnlyDataVariable As UADataVariable =
New UADataVariable("SByteValue").Writable(False).ValueType(Of SByte)()
Dim stringReadOnlyDataVariable As UADataVariable =
New UADataVariable("StringValue").Writable(False).ValueType(Of String)()
Dim uInt16ReadOnlyDataVariable As UADataVariable =
New UADataVariable("UInt16Value").Writable(False).ValueType(Of UShort)()
Dim uInt32ReadOnlyDataVariable As UADataVariable =
New UADataVariable("UInt32Value").Writable(False).ValueType(Of UInteger)()
Dim uInt64ReadOnlyDataVariable As UADataVariable =
New UADataVariable("UInt64Value").Writable(False).ValueType(Of ULong)()
Dim variantReadOnlyDataVariable As UADataVariable =
New UADataVariable("VariantValue").Writable(False)
' Create Constant sub-folder under the Data folder. It contains read-only data variables with constant values.
dataFolder.Add(
New UAFolder("Constant") From
{
New UAFolder("Scalar") From
{
New UADataVariable("BooleanValue").ConstantValue(True),
New UADataVariable("ByteStringValue").ConstantValue(New Byte() {&H57, &H21, &H40, &HFC}),
New UADataVariable("ByteValue").ConstantValue(CType(144, Byte)) _
, _ ' We are passing In UTC times, because we want always the same result, And so we must specify
_ ' the DateTimeKind. You can pass in local times, but then they will be converted to UTC by the
_ ' server, And the result will depend on the time zone.
New UADataVariable("DateTimeValue").ConstantValue(
DateTime.SpecifyKind(New DateTime(2024, 7, 12, 14, 4, 55).AddSeconds(0.444),
DateTimeKind.Utc)), _
_
New UADataVariable("DoubleValue").ConstantValue(0.0000000000775630105797),
New UADataVariable("FloatValue").ConstantValue(2.77002E+29F),
New UADataVariable("GuidValue").ConstantValue(
New Guid("{1AEF59AE-5029-42A7-9AE2-B2DC00072999}")),
New UADataVariable("Int16Value").ConstantValue(CType(-30956, Short)),
New UADataVariable("Int32Value").ConstantValue(276673160),
New UADataVariable("Int64Value").ConstantValue(1412096336825367659),
New UADataVariable("SByteValue").ConstantValue(CType(-113, SByte)),
New UADataVariable("StringValue").ConstantValue("lorem ipsum"),
New UADataVariable("UInt16Value").ConstantValue(CType(64421, UShort)),
New UADataVariable("UInt32Value").ConstantValue(3853116537UI),
New UADataVariable("UInt64Value").ConstantValue(9431348106520835314UL),
New UADataVariable("VariantValue").ConstantValue(529739609)
}
})
' Create Dynamic sub-folder under the Data folder. It contains data variables with dynamically changing values.
' This is a tricky case. We want array of Byte-s, but that is automatically recognized as scalar
' OPC UA ByteString. For a true array of Byte-s, the data type Id and array dimension list must be
' specified explicitly.
dataFolder.Add(
New UAFolder("Dynamic") From
{
New UAFolder("Array") From
{
New UADataVariable("BooleanValue").ReadValueFunction(Function() NextRandomArray(AddressOf NextRandomBoolean)),
New UADataVariable("ByteStringValue").ReadValueFunction(Function() NextRandomArray(AddressOf NextRandomByteString)) _
, _ ' This is a tricky case. We want array of Byte-s, but that is automatically recognized as scalar
_ ' OPC UA ByteString. For a true array of Byte-s, the data type Id and array dimension list must be
_ ' specified explicitly.
New UADataVariable("ByteValue").ReadValueFunction(
dataTypeId:=UADataTypeIds.Byte,
arrayRank:=1,
Function() NextRandomArray(AddressOf NextRandomByte)),
New UADataVariable("DateTimeValue").ReadValueFunction(Function() NextRandomArray(AddressOf NextRandomDateTime)),
New UADataVariable("DoubleValue").ReadValueFunction(Function() NextRandomArray(AddressOf NextRandomDouble)),
New UADataVariable("FloatValue").ReadValueFunction(Function() NextRandomArray(AddressOf NextRandomFloat)),
New UADataVariable("GuidValue").ReadValueFunction(Function() NextRandomArray(AddressOf NextRandomGuid)),
New UADataVariable("Int16Value").ReadValueFunction(Function() NextRandomArray(AddressOf NextRandomInt16)),
New UADataVariable("Int32Value").ReadValueFunction(Function() NextRandomArray(AddressOf NextRandomInt32)),
New UADataVariable("Int64Value").ReadValueFunction(Function() NextRandomArray(AddressOf NextRandomInt64)),
New UADataVariable("SByteValue").ReadValueFunction(Function() NextRandomArray(AddressOf NextRandomSByte)),
New UADataVariable("StringValue").ReadValueFunction(Function() NextRandomArray(AddressOf NextRandomString)),
New UADataVariable("UInt16Value").ReadValueFunction(Function() NextRandomArray(AddressOf NextRandomUInt16)),
New UADataVariable("UInt32Value").ReadValueFunction(Function() NextRandomArray(AddressOf NextRandomUInt32)),
New UADataVariable("UInt64Value").ReadValueFunction(Function() NextRandomArray(AddressOf NextRandomUInt64)),
New UADataVariable("VariantValue").ReadValueFunction(Function() NextRandomArray(NextRandomVariant))
},
New UAFolder("Scalar") From
{
New UADataVariable("BooleanValue").ReadValueFunction(AddressOf NextRandomBoolean),
New UADataVariable("ByteStringValue").ReadValueFunction(AddressOf NextRandomByteString),
New UADataVariable("ByteValue").ReadValueFunction(AddressOf NextRandomByte),
New UADataVariable("DateTimeValue").ReadValueFunction(AddressOf NextRandomDateTime),
New UADataVariable("DoubleValue").ReadValueFunction(AddressOf NextRandomDouble),
New UADataVariable("FloatValue").ReadValueFunction(AddressOf NextRandomFloat),
New UADataVariable("GuidValue").ReadValueFunction(AddressOf NextRandomGuid),
New UADataVariable("Int16Value").ReadValueFunction(AddressOf NextRandomInt16),
New UADataVariable("Int32Value").ReadValueFunction(AddressOf NextRandomInt32),
New UADataVariable("Int64Value").ReadValueFunction(AddressOf NextRandomInt64),
New UADataVariable("SByteValue").ReadValueFunction(AddressOf NextRandomSByte),
New UADataVariable("StringValue").ReadValueFunction(AddressOf NextRandomString),
New UADataVariable("UInt16Value").ReadValueFunction(AddressOf NextRandomUInt16),
New UADataVariable("UInt32Value").ReadValueFunction(AddressOf NextRandomUInt32),
New UADataVariable("UInt64Value").ReadValueFunction(AddressOf NextRandomUInt64),
New UADataVariable("VariantValue").ReadValueFunction(AddressOf NextRandomVariant)
}
})
' The FullyWritable sub-folder contains data variables that have not only writable value, but also writable
' source timestamp and status code.
dataFolder.Add(
New UAFolder("FullyWritable") From
{
New UAFolder("Scalar") From
{
New UADataVariable("BooleanValue").ReadWriteValue(True) _
.Writable(True, True, True),
New UADataVariable("ByteStringValue").ReadWriteValue(New Byte() {&H57, &H21, &H40, &HFC}) _
.Writable(True, True, True),
New UADataVariable("ByteValue").ReadWriteValue(CType(144, Byte)) _
.Writable(True, True, True) _
, _ ' We are passing in UTC times, because we want always the same result, and so we must specify
_ ' the DateTimeKind. You can pass in local times, but then they will be converted to UTC by the
_ ' server, and the result will depend on the time zone.
New UADataVariable("DateTimeValue").ReadWriteValue(
DateTime.SpecifyKind(New DateTime(2024, 7, 12, 14, 4, 55).AddSeconds(0.444),
DateTimeKind.Utc)) _
.Writable(True, True, True),
New UADataVariable("DoubleValue").ReadWriteValue(0.0000000000775630105797) _
.Writable(True, True, True),
New UADataVariable("FloatValue").ReadWriteValue(2.77002E+29F) _
.Writable(True, True, True),
New UADataVariable("GuidValue") _
.ReadWriteValue(New Guid("{1AEF59AE-5029-42A7-9AE2-B2DC00072999}")) _
.Writable(True, True, True),
New UADataVariable("Int16Value").ReadWriteValue(CType(-30956, Short)) _
.Writable(True, True, True),
New UADataVariable("Int32Value").ReadWriteValue(276673160) _
.Writable(True, True, True),
New UADataVariable("Int64Value").ReadWriteValue(1412096336825367659) _
.Writable(True, True, True),
New UADataVariable("SByteValue").ReadWriteValue(CType(-113, SByte)) _
.Writable(True, True, True),
New UADataVariable("StringValue").ReadWriteValue("lorem ipsum") _
.Writable(True, True, True),
New UADataVariable("UInt16Value").ReadWriteValue(CType(64421, UShort)) _
.Writable(True, True, True),
New UADataVariable("UInt32Value").ReadWriteValue(3853116537UI) _
.Writable(True, True, True),
New UADataVariable("UInt64Value").ReadWriteValue(9431348106520835314UL) _
.Writable(True, True, True),
New UADataVariable("VariantValue").ReadWriteValue(529739609) _
.Writable(True, True, True)
}
})
' The ReadOnly sub-folder contains data variables that are read-only, and their values can be changed through
' corresponding data variables in the WriteOnly sub-folder.
dataFolder.Add(
New UAFolder("ReadOnly") From
{
New UAFolder("Scalar") From
{
booleanReadOnlyDataVariable,
byteStringReadOnlyDataVariable,
byteReadOnlyDataVariable,
dateTimeReadOnlyDataVariable,
doubleReadOnlyDataVariable,
floatReadOnlyDataVariable,
guidReadOnlyDataVariable,
int16ReadOnlyDataVariable,
int32ReadOnlyDataVariable,
int64ReadOnlyDataVariable,
sByteReadOnlyDataVariable,
stringReadOnlyDataVariable,
uInt16ReadOnlyDataVariable,
uInt32ReadOnlyDataVariable,
uInt64ReadOnlyDataVariable,
variantReadOnlyDataVariable
}
})
' The Static sub-folder contains data variables with static values which can be changed through writing to
' them (so-called "registers").
' "Array":
' For demonstration, we consistently create one-dimensional arrays with initially 3 elements, where the
' first element has the same value as the scalar variable with the same name.
' "Array.ByteValue":
' This is a tricky case. We want array of Byte-s, but that is automatically recognized as scalar
' OPC UA ByteString. For a true array of Byte-s, the data type Id and array dimension list must be
' specified explicitly.
' "Array.DateTimeValue":
' We are passing in UTC times, because we want always the same result, and so we must specify
' the DateTimeKind. You can pass in local times, but then they will be converted to UTC by the
' server, and the result will depend on the time zone.
' "Array2D":
' We create 2-dimensional arrays with 4x3 size, and default element values.
' "BoundedArray":
' Array nodes with specified and enforced maximum array dimensions.
' "BoundedArray.ByteValue":
' This is a tricky case. We want array of Byte-s, but that is automatically recognized as scalar
' OPC UA ByteString. For a true array of Byte-s, the data type Id and array dimension list must be
' specified explicitly.
' "Scalar.DateTimeValue":
' We are passing in UTC times, because we want always the same result, and so we must specify
' the DateTimeKind. You can pass in local times, but then they will be converted to UTC by the
' server, and the result will depend on the time zone.
dataFolder.Add(
New UAFolder("Static") From
{ _
_ ' For demonstration, we consistently create one-dimensional arrays with initially 3 elements, where the
_ ' first element has the same value as the scalar variable with the same name.
New UAFolder("Array") From
{
New UADataVariable("BooleanValue").ReadWriteValue(
{
True,
False,
True
}),
New UADataVariable("ByteStringValue").ReadWriteValue(
{
New Byte() {&H57, &H21, &H40, &HFC},
New Byte() {248, 131, 217, 210},
New Byte() {252, 152, 119, 65}
}), _
_ ' This is a tricky case. We want array of Byte-s, but that is automatically recognized as scalar
_ ' OPC UA ByteString. For a true array of Byte-s, the data type Id and array dimension list must be
_ ' specified explicitly.
New UADataVariable("ByteValue").ReadWriteValue(
dataTypeId:=UADataTypeIds.Byte,
arrayRank:=1,
value:=New Byte() _
{
144,
19,
233
}),
New UADataVariable("DateTimeValue").ReadWriteValue(
{ _
_ ' We are passing in UTC times, because we want always the same result, and so we must specify
_ ' the DateTimeKind. You can pass in local times, but then they will be converted to UTC by the
_ ' server, and the result will depend on the time zone.
DateTime.SpecifyKind(New DateTime(2024, 7, 12, 14, 4, 55).AddSeconds(0.444),
DateTimeKind.Utc),
DateTime.SpecifyKind(New DateTime(2024, 4, 8), DateTimeKind.Utc),
DateTime.SpecifyKind(New DateTime(2023, 8, 14, 18, 13, 0), DateTimeKind.Utc)
}),
New UADataVariable("DoubleValue").ReadWriteValue(
{
0.0000000000775630105797,
-0.467227097818268,
-3.51653052582609E+300
}),
New UADataVariable("FloatValue").ReadWriteValue(
{
2.77002E+29F,
-1.103936E+36F,
-9.002293E-28F
}),
New UADataVariable("GuidValue").ReadWriteValue(
{
New Guid("{1AEF59AE-5029-42A7-9AE2-B2DC00072999}"),
New Guid("{E8690EA3-25D0-4F19-9DFC-AA25D2772B2F}"),
New Guid("{9E081C84-7953-4A88-B709-447FC187EDD9}")
}),
New UADataVariable("Int16Value").ReadWriteValue(New Short() _
{
-30956,
31277,
21977
}),
New UADataVariable("Int32Value").ReadWriteValue(
{
276673160,
630080334,
-391755284
}),
New UADataVariable("Int64Value").ReadWriteValue(
{
1412096336825367659,
-808781653700434592,
4707848393174903135
}),
New UADataVariable("SByteValue").ReadWriteValue(New SByte() _
{
-113,
-92,
2
}),
New UADataVariable("StringValue").ReadWriteValue(
{
"lorem ipsum",
"dolor sit amet",
"consectetur adipiscing elit"
}),
New UADataVariable("UInt16Value").ReadWriteValue(New UShort() _
{
64421US,
22663US,
36755US
}),
New UADataVariable("UInt32Value").ReadWriteValue(New UInteger() _
{
3853116537UI,
968679231UI,
995611904UI
}),
New UADataVariable("UInt64Value").ReadWriteValue(New ULong() _
{
9431348106520835314UL,
15635738044048254300UL,
946287779964705249UL
}),
New UADataVariable("VariantValue").ReadWriteValue(New Object() _
{
529739609,
"lorem ipsum",
New Guid("{1AEF59AE-5029-42A7-9AE2-B2DC00072999}")
})
}, _
_
_ ' We create 2-dimensional arrays with 4x3 size, and default element values.
New UAFolder("Array2D") From
{
New UADataVariable("BooleanValue").ReadWriteValue(New Boolean(3, 2) {}),
New UADataVariable("ByteStringValue").ReadWriteValue(New Byte(3, 2)() {}),
New UADataVariable("ByteValue").ReadWriteValue(New Byte(3, 2) {}),
New UADataVariable("DateTimeValue").ReadWriteValue(New DateTime(3, 2) {}),
New UADataVariable("DoubleValue").ReadWriteValue(New Double(3, 2) {}),
New UADataVariable("FloatValue").ReadWriteValue(New Single(3, 2) {}),
New UADataVariable("GuidValue").ReadWriteValue(New Guid(3, 2) {}),
New UADataVariable("Int16Value").ReadWriteValue(New Short(3, 2) {}),
New UADataVariable("Int32Value").ReadWriteValue(New Integer(3, 2) {}),
New UADataVariable("Int64Value").ReadWriteValue(New Long(3, 2) {}),
New UADataVariable("SByteValue").ReadWriteValue(New SByte(3, 2) {}),
New UADataVariable("StringValue").ReadWriteValue(New String(3, 2) {}),
New UADataVariable("UInt16Value").ReadWriteValue(New UShort(3, 2) {}),
New UADataVariable("UInt32Value").ReadWriteValue(New UInteger(3, 2) {}),
New UADataVariable("UInt64Value").ReadWriteValue(New ULong(3, 2) {}),
New UADataVariable("VariantValue").ReadWriteValue(New Object(3, 2) {})
}, _
_
_ ' Array nodes with specified and enforced maximum array dimensions.
New UAFolder("BoundedArray") From
{
New UADataVariable("BooleanValue").ReadWriteValue(New Boolean(3) {}, 5),
New UADataVariable("ByteStringValue").ReadWriteValue(New Byte(3)() {}, 5) _
, _
_ ' This is a tricky case. We want array of Byte-s, but that is automatically recognized as scalar
_ ' OPC UA ByteString. For a true array of Byte-s, the data type Id and array dimension list must be
_ ' specified explicitly.
New UADataVariable("ByteValue").ReadWriteValue(
dataTypeId:=UADataTypeIds.Byte,
arrayDimensionList:={5},
New Byte(3) {}),
New UADataVariable("DateTimeValue").ReadWriteValue(New DateTime(3) {}, 5),
New UADataVariable("DoubleValue").ReadWriteValue(New Double(3) {}, 5),
New UADataVariable("FloatValue").ReadWriteValue(New Single(3) {}, 5),
New UADataVariable("GuidValue").ReadWriteValue(New Guid(3) {}, 5),
New UADataVariable("Int16Value").ReadWriteValue(New Short(3) {}, 5),
New UADataVariable("Int32Value").ReadWriteValue(New Integer(3) {}, 5),
New UADataVariable("Int64Value").ReadWriteValue(New Long(3) {}, 5),
New UADataVariable("SByteValue").ReadWriteValue(New SByte(3) {}, 5),
New UADataVariable("StringValue").ReadWriteValue(New String(3) {}, 5),
New UADataVariable("UInt16Value").ReadWriteValue(New UShort(3) {}, 5),
New UADataVariable("UInt32Value").ReadWriteValue(New UInteger(3) {}, 5),
New UADataVariable("UInt64Value").ReadWriteValue(New ULong(3) {}, 5),
New UADataVariable("VariantValue").ReadWriteValue(New Object(3) {}, 5)
}, _
_
New UAFolder("Scalar") From
{
New UADataVariable("BooleanValue").ReadWriteValue(True),
New UADataVariable("ByteStringValue").ReadWriteValue(New Byte() {&H57, &H21, &H40, &HFC}),
New UADataVariable("ByteValue").ReadWriteValue(CByte(144)) _
, _
_ ' We are passing in UTC times, because we want always the same result, and so we must specify
_ ' the DateTimeKind. You can pass in local times, but then they will be converted to UTC by the
_ ' server, and the result will depend on the time zone.
New UADataVariable("DateTimeValue").ReadWriteValue(
DateTime.SpecifyKind(New DateTime(2024, 7, 12, 14, 4, 55).AddSeconds(0.444),
DateTimeKind.Utc)),
New UADataVariable("DoubleValue").ReadWriteValue(0.0000000000775630105797),
New UADataVariable("FloatValue").ReadWriteValue(2.77002E+29F),
New UADataVariable("GuidValue").ReadWriteValue(
New Guid("{1AEF59AE-5029-42A7-9AE2-B2DC00072999}")),
New UADataVariable("Int16Value").ReadWriteValue(CType(-30956, Short)),
New UADataVariable("Int32Value").ReadWriteValue(276673160),
New UADataVariable("Int64Value").ReadWriteValue(1412096336825367659),
New UADataVariable("SByteValue").ReadWriteValue(CType(-113, SByte)),
New UADataVariable("StringValue").ReadWriteValue("lorem ipsum"),
New UADataVariable("UInt16Value").ReadWriteValue(CType(64421, UShort)),
New UADataVariable("UInt32Value").ReadWriteValue(3853116537UI),
New UADataVariable("UInt64Value").ReadWriteValue(9431348106520835314UL),
New UADataVariable("VariantValue").ReadWriteValue(529739609)
}
})
' Create and add write-only data variables of various data types. Implement write actions that write the value
' to the corresponding read-only data variable of the same data type.
dataFolder.Add(
New UAFolder("WriteOnly") From
{
New UAFolder("Scalar") From
{
New UADataVariable("BooleanValue").Readable(False).WriteValueAction(Sub(value As Boolean) _
booleanReadOnlyDataVariable.UpdateReadAttributeData(value)),
New UADataVariable("ByteStringValue").Readable(False).WriteValueAction(Sub(value As Byte()) _
byteStringReadOnlyDataVariable.UpdateReadAttributeData(value)),
New UADataVariable("ByteValue").Readable(False).WriteValueAction(Sub(value As Byte) _
byteReadOnlyDataVariable.UpdateReadAttributeData(value)),
New UADataVariable("DateTimeValue").Readable(False).WriteValueAction(Sub(value As DateTime) _
dateTimeReadOnlyDataVariable.UpdateReadAttributeData(value)),
New UADataVariable("DoubleValue").Readable(False).WriteValueAction(Sub(value As Double) _
doubleReadOnlyDataVariable.UpdateReadAttributeData(value)),
New UADataVariable("FloatValue").Readable(False).WriteValueAction(Sub(value As Single) _
floatReadOnlyDataVariable.UpdateReadAttributeData(value)),
New UADataVariable("GuidValue").Readable(False).WriteValueAction(Sub(value As Guid) _
guidReadOnlyDataVariable.UpdateReadAttributeData(value)),
New UADataVariable("Int16Value").Readable(False).WriteValueAction(Sub(value As Short) _
int16ReadOnlyDataVariable.UpdateReadAttributeData(value)),
New UADataVariable("Int32Value").Readable(False).WriteValueAction(Sub(value As Integer()) _
int32ReadOnlyDataVariable.UpdateReadAttributeData(value)),
New UADataVariable("Int64Value").Readable(False).WriteValueAction(Sub(value As Long) _
int64ReadOnlyDataVariable.UpdateReadAttributeData(value)),
New UADataVariable("SByteValue").Readable(False).WriteValueAction(Sub(value As SByte) _
sByteReadOnlyDataVariable.UpdateReadAttributeData(value)),
New UADataVariable("StringValue").Readable(False).WriteValueAction(Sub(value As String) _
stringReadOnlyDataVariable.UpdateReadAttributeData(value)),
New UADataVariable("UInt16Value").Readable(False).WriteValueAction(Sub(value As UShort) _
uInt16ReadOnlyDataVariable.UpdateReadAttributeData(value)),
New UADataVariable("UInt32Value").Readable(False).WriteValueAction(Sub(value As UInteger) _
uInt32ReadOnlyDataVariable.UpdateReadAttributeData(value)),
New UADataVariable("UInt64Value").Readable(False).WriteValueAction(Sub(value As ULong) _
uInt64ReadOnlyDataVariable.UpdateReadAttributeData(value)),
New UADataVariable("VariantValue").Readable(False).WriteValueAction(Sub(value As Object) _
variantReadOnlyDataVariable.UpdateReadAttributeData(value))
}
})
End Sub
' Random value generators.
Private ReadOnly Random As Random = New Random()
Private ReadOnly RandomStrings As String() = {"lorem", "ipsum", "dolor", "sit", "amet"}
Private Function NextRandomArray(Of T)(nextRandomElement As Func(Of T)) As T()
Return {nextRandomElement(), nextRandomElement(), nextRandomElement()}
End Function
Private Function NextRandomBoolean() As Boolean
Return Random.Next(2) <> 0
End Function
Private Function NextRandomByte() As Byte
Return CType(Random.Next(Byte.MinValue, Byte.MaxValue + 1), Byte)
End Function
Private Function NextRandomByteString() As Byte()
Return {NextRandomByte(), NextRandomByte(), NextRandomByte(), NextRandomByte()}
End Function
Private Function NextRandomDateTime() As DateTime
Return DateTime.MinValue.AddMilliseconds((DateTime.MaxValue - DateTime.MinValue).TotalMilliseconds *
Random.NextDouble())
End Function
Private Function NextRandomFloat() As Single
Return CType(Math.Pow(10, Math.Log10(Single.MaxValue) * Random.NextDouble()) * (2 * Random.Next(2) - 1), Single)
End Function
Private Function NextRandomDouble() As Double
Return Math.Pow(10, Math.Log10(Double.MaxValue) * Random.NextDouble()) * (2 * Random.Next(2) - 1)
End Function
Private Function NextRandomGuid() As Guid
Return Guid.NewGuid()
End Function
Private Function NextRandomInt16() As Short
Return CType(Random.Next(Short.MinValue, Short.MaxValue + 1), Short)
End Function
Private Function NextRandomInt32() As Integer
Dim buffer As Byte() = New Byte(3) {}
Random.NextBytes(buffer)
Return BitConverter.ToInt32(buffer, 0)
End Function
Private Function NextRandomInt64() As Long
Dim buffer As Byte() = New Byte(7) {}
Random.NextBytes(buffer)
Return BitConverter.ToInt64(buffer, 0)
End Function
Private Function NextRandomSByte() As SByte
Return CType(Random.Next(SByte.MinValue, SByte.MaxValue + 1), SByte)
End Function
Private Function NextRandomString() As String
Return RandomStrings(Random.Next(RandomStrings.Length))
End Function
Private Function NextRandomUInt16() As UShort
Return CType(Random.Next(UShort.MinValue, UShort.MaxValue + 1), UShort)
End Function
Private Function NextRandomUInt32() As UInteger
Dim buffer As Byte() = New Byte(3) {}
Random.NextBytes(buffer)
Return BitConverter.ToUInt32(buffer, 0)
End Function
Private Function NextRandomUInt64() As ULong
Dim buffer As Byte() = New Byte(7) {}
Random.NextBytes(buffer)
Return BitConverter.ToUInt64(buffer, 0)
End Function
Private Function NextRandomVariant() As Object
Select Case Random.Next(15)
Case 0
Return NextRandomBoolean()
Case 1
Return NextRandomByteString()
Case 2
Return NextRandomByte()
Case 3
Return NextRandomDateTime()
Case 4
Return NextRandomDouble()
Case 5
Return NextRandomFloat()
Case 6
Return NextRandomGuid()
Case 7
Return NextRandomInt16()
Case 8
Return NextRandomInt32()
Case 9
Return NextRandomInt64()
Case 10
Return NextRandomSByte()
Case 11
Return NextRandomString()
Case 12
Return NextRandomUInt16()
Case 13
Return NextRandomUInt32()
Case 14
Return NextRandomUInt64()
Case Else
Return Nothing
End Select
End Function
End Module
'End Namespace